package com.xiayuanxing.arithmetic.DataStructures.Stack;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @program: arithmetic
 * @description: 逆波兰计算器
 * @author: xia yuan xing
 * @create: 2021-09-13 15:04
 */
public class PolandNotation {

    public static void main(String[] args) {

//        //先定义逆波兰表达式
//        //(3+4)*5-6   ===> 3 4 + 5 * 6 -
//        String suffixExpression = "4 5 * 8 - 60 + 8 2 / +";
//        //1.先将表达式放到ArrayList中，将ArrayList传递给一个方法 配合栈完成计算
//        List<String> strings = getListString(suffixExpression);
//        int result = calculate(strings);
//        System.out.println("result:"+result);


        String expression =("1+((2+3)*4)-5");
        List<String> list = toInfixExpressionList(expression);
        System.out.println("转换成功后："+list.toString());

        List<String> suffixExpression= parseSuffixExpressionList(list);
        System.out.println("转换后的逆波兰表达式："+suffixExpression.toString());

        int calculate = calculate(suffixExpression);
        System.out.println("计算的结果是："+calculate);


    }

    /**
     * 将逆波兰表达式的数据和运算符放入ArrayList中
     * @param suffixExpression
     * @return
     */
    public static List<String> getListString(String suffixExpression){
        //将suffixExpression分割
        String[] split = suffixExpression.split(" ");

        List<String> list = new ArrayList<>();
        for (String s : split) {
            list.add(s);
        }
        return list;
    }

    /**
     * 将中缀表达式转换成对应的list
     * @param s
     * @return
     */
    public static List<String> toInfixExpressionList(String s){
        //定义一个list
        List<String> list = new ArrayList<>();
        //指针：用于遍历中缀表达式字符串
        int i = 0;
        //多位数拼接
        String str;
        //每遍历一个字符，就放入c
        char c;

        do {
            //如果c是一个非数字，则需要加入到list
            //'0'[48] ->'9'[57]
            if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57){
                list.add(c + "");
                i++;
            }else {
                //是数字，判断多位数
                //将str置为空
                str = "";
                while (i < s.length() && (c = s.charAt(i)) >= 48 && (c = s.charAt(i)) <= 57){
                    str += c;
                    i++;
                }
                list.add(str);
            }

        }while (i<s.length());
        return list;
    }

    /**
     * 将中缀表达式list转换成逆波兰表达式list
     */
    public static List<String> parseSuffixExpressionList(List<String> ls){

        //初始化符号栈
        Stack<String> s1 = new Stack<>();
        //用于存储中间结果
        List<String>  s2 = new ArrayList<>();

        //遍历ls
        for (String item : ls) {
            //如果是一个数，加入s2
            if (item.matches("\\d+")){
                s2.add(item);
            }else if (item.equals("(")){
                s1.push(item);
            }else if (item.equals(")")){
                //如果是右括号，将符号栈顶的符号依次压入s2，直到出现左括号为止，将这对括号丢弃
                while (!s1.peek().equals("(")){
                    //向s2中压入值
                    s2.add(s1.pop());
                }
                //将左括号弹出栈，消除这对括号
                s1.pop();
            }else {
                //当item的优先级小于等于s1栈顶运算符时，将s1栈顶的运算符弹出
                //并加入到s2中，在继续循环比较压入
                while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
                    s2.add(s1.pop());
                }
                //将item入栈
                s1.push(item);
            }
        }
        //将s1中剩余的运算符依次弹出并加入s2中
        while (s1.size() != 0){
            s2.add(s1.pop());
        }
        return s2;
    }

    /**
     * 1.从左至右扫描，将3和4压入堆栈
     * 2.遇到+运算符，因此弹出4和3，计算出3+4的值，得7，在将7入栈
     * 3.将5入栈
     * 4.接下来是*运算符，因此弹出5和7，计算出5*7=35，将35入栈；
     * 5.将6入栈
     * 6.最后是-运算符，计算出35-6的值，即29，由此得出最终结果
     */
    /**
     * 计算器(放入逆波兰表达式数组)
     * @param ls
     * @return
     */
    public static int calculate(List<String> ls){
        //创建一个栈
        Stack<String> stack = new Stack<>();
        //遍历ls
        for (String item:ls){
            //使用正则表达式来取数
            if(item.matches("\\d+")){
                //匹配的是多位数，进行入栈操作
                stack.push(item);
            }else {
                //pop出两个数，并运算，再入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if (item.equals("+")){
                    res = num1 + num2;
                }else if (item.equals("-")){
                    res = num1 - num2;
                }else if (item.equals("*")){
                    res = num1 * num2;
                }else if (item.equals("/")){
                    res = num1 / num2;
                }else {
                    throw  new RuntimeException("运算符错误");
                }
                //将运算的值入栈
                stack.push(res+"");
            }
        }
        return Integer.parseInt(stack.pop());
    }




}


/**
 * 运算符优先级类
 */
class Operation{

    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 1;
    private static int DIV = 1;


    /**
     * 返回对应的优先级数字
     * @param st
     * @return
     */
    public static int getValue(String st){
        int result = 0;
        switch (st){
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                break;
        }
        return result;
    }

}